#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
from tkinter.messagebox import askyesno

import click
import numpy as np
from cryptography.fernet import Fernet
from datetime import datetime
import time
from sympy.physics.units import volume

from utility import datestrFormat
from autoPilot import Pilot
from utility import ExcelFiles
import tkinter as tk
from tkinter import filedialog, simpledialog
from tkinter.messagebox import askyesno
from config import config, getConfigEnvs, commonConfig
from tkinter import messagebox
import pandas as pd
import sys
import traceback

envs = getConfigEnvs()
cfg = commonConfig()
root = tk.Tk()
root.withdraw()  # 隐藏主窗口
root.attributes("-topmost", True)  # 设置主窗口为置顶
root.focus_force()  # 强制主窗口获得焦点，对话框会继承激活状态
# print("envs:",envs)
# print("cfg:",cfg)
def authorization():
    try:
        cipher_suite = Fernet('-AtUkQo5RB3-qd1WW2HaqLJW3Eiq7IjJoBeVeaEDmlU=')
        expiry_date = cipher_suite.decrypt(cfg['auth_key']).decode('utf-8')
        current_time =  datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print("{}比较{}".format(current_time, expiry_date))
        if current_time > expiry_date:
            messagebox.showerror(title="错误", message="授权已过期，请更新授权码！！")
            sys.exit(1)
    except:
        messagebox.showerror(title="错误", message="授权已过期，请更新授权码！！")
        sys.exit(1)

def valueRange(valueList: list):
   class valueRangeClass(click.ParamType):
        name = "custom"
        def convert(self, value, param, ctx):
            try:
                # 在这里添加你的校验逻辑
                if value not in valueList :
                    self.fail(f"{value} 值无效，请重新输入！", param, ctx)
                return value
            except ValueError:
                self.fail(f"{value}不在有效范围，{self.name}", param, ctx)
   return valueRangeClass()

def validate_range(ctx, param, value):
    # 获取前面的选项值
    mode = ctx.params.get("mode")
    if mode == "low":
        if not (0 <= value <= 10):
            raise click.BadParameter("范围必须是 0 到 10")
    elif mode == "high":
        if not (10 < value <= 100):
            raise click.BadParameter("范围必须是 11 到 100")
    return value

@click.command()
@click.option('--webtype', default="warehouse", type=click.Choice(["warehouse", "yupei"], case_sensitive=False))
@click.option('--env', default="hkf", type=click.Choice(envs, case_sensitive=False))
@click.option('--excelfile')
@click.option('--headless', default="N", type=click.Choice(["Y", "N"], case_sensitive=False), prompt='是否静默模式？(Y/N)：' , help='是否静默模式？(Y/N)：')
# @click.option('--check_if_exists', default="0", type=valueRange(["0","1","2","3"]), prompt='填单前,是否检查该单号已经存在？(0/1/2) 0-不检查，1-批量检查，2-逐单检查，3-仅检查不填单：', help='填单前,是否检查该单号已经存在？(0/1/2) 0-不检查，1-批量检查，2-逐单检查：')
def main(webtype, env, excelfile, headless):
    # 使用动态的 prompt 的option
    _, login_info, column_mapping, platforms = config(env)
    login_info['url_yupei'] = cfg['url_yupei'];login_info['url_warehouse'] = cfg['url_warehouse'];login_info['receiving_station'] = cfg['receiving_station']
    # print("login_info:",login_info)
    name_prompt = '请选择数据来源是哪个平台？{}'.format(",".join([f"{k}-{v}" for k,v in platforms.items() if v!="default" ]))
    default_platform_choice = next((k for k, v in platforms.items() if v == column_mapping.get("default","PDD")), None)
    # print("default_platform_choice:",default_platform_choice)
    """
     动态设置prompt部分：
    """
    # 除default外，只有一种平台；则不选
    if len([ v for k,v in platforms.items() if v!="default" ]) > 1:
        platform = click.prompt(name_prompt, default=default_platform_choice, type=valueRange(platforms.keys()))
    else:
        platform = default_platform_choice
    # 非仓库系统则不选
    if webtype == "warehouse":
        name_prompt = '填单前,是否检查该单号已经存在？(0/1/2) 0-不检查，1-批量检查，2-逐单检查，3-仅检查不填单：'
        check_if_exists = click.prompt(name_prompt, default="0", type=valueRange(["0","1","2","3"]))
    else:
        name_prompt = '是否检查主单号已经存在并提交申报？(Y/N) Y-申报, N-不申报'
        login_info["preCheck"] = click.prompt(name_prompt, default="Y", type=click.Choice(["N","Y"], case_sensitive=False))
        name_prompt = '是否仅填写航班编号头部信息？(Y/N) N-否，Y-是'
        login_info["onlyFlightCode"] = click.prompt(name_prompt, default="N", type=click.Choice(["N","Y"], case_sensitive=False))
        check_if_exists = "0"
    # print("platform type index:", platform)
    # print(type(check_if_exists), type(headless), type(platform))
    def get_user_input():
        root = tk.Tk()
        root.withdraw()  # 隐藏主窗口
        file_path = filedialog.askopenfilename(filetypes=[("excel files", "*.xlsx *.xls")])  # 打开文件选择对话框，只允许选择 Excel 文件
        # check_if_exists = simpledialog.askstring("输入","每次填单前,是否检查该单号已经存在？(Y/N)：").strip() or "N"
        # check_total = simpledialog.askstring("输入","填单结束后,是否总单数检查？(Y/N)：").strip() or "N"
        return file_path

    def ask_retry_with_topmost(prompt_str:str, ):
        user_input = simpledialog.askstring("输入",  prompt_str, parent=root)
        root.attributes("-topmost", False) # 取消置顶 return
        return user_input

    def login(auto_pilot):
        retry_times=0
        while True:
            try:
                if retry_times > 5:
                    print("登录失败！且超过最大尝试次数！")
                    auto_pilot.quit()
                    sys.exit(1)
                auto_pilot.login()
                break
            except:
                traceback.print_exc()
                continue
    # 去除ACL提示
    # 获取filePath
    pf = platforms[platform]
    print("platform type:", pf)
    filePath = ""
    if pf == "Excel接口":
        if not excelfile:
            print("没有指定Excel接口文件路径！")
            return -1
        print("excelfile:", excelfile)
        # excelfile = "C:\\Users\\60521\\AppData\\Local\\Temp\\Temp20250309.xlsx"
        # filePath = "\\".join(re.split(r"\\+", excelfile))
        filePath = excelfile
        print("临时Excel接口:", filePath)
    else:
        tried_times = 0
        while filePath == "":
            tried_times += 1
            if tried_times> 3:
                print("选择文件失败且超过最大尝试次数！")
                return -1
            try:
                filePath = get_user_input()
            except Exception as e:
                print(e)
                return -1

    print("platform:", platform)
    print(f"选择的数据源模板是：{filePath}")
    files = [
        {"name": 'sample', "path": filePath, "sheet_name": 0},
    ]
    dfs = ExcelFiles(files)
    mp = column_mapping[pf] or dict()
    cargo_type_head = mp.get('货物类型','货物类型')
    pingMing_head = mp.get("品名", "品名")
    flightNo_head = mp.get('航班号航班日期', '航班号航班日期')
    billNo_head = mp.get("主单号", "主单号")
    subBillNo_head = mp.get("分单号", "分单号")
    weight_head = mp.get("重量", "重量")
    article_head = mp.get("件数", "件数")
    volume_head = mp.get("体积", "体积")
    destPort_head = mp.get("货物目的地三字代码", "货物目的地三字代码")
    dimension_head = mp.get("尺寸数量", "尺寸数量")
    for sheet, df in dfs.items():
        # 无法解析的字段名处理
        for c in df.columns:
            if '\n' in c or '/' in c:
                df.rename(columns={c: c.replace('\n', '').replace('/', '')}, inplace=True)
        if flightNo_head not in df.columns:
            err_info = "表格少【航班号航班日期】列，请补充！列值的格式示例：5Y594/10.31"
            print(err_info)
            messagebox.showinfo("错误提示！", err_info)
            return -1
        df[billNo_head] = df[billNo_head].ffill().bfill()
        df[pingMing_head] = df[pingMing_head].ffill().bfill()
        df[flightNo_head] = df[flightNo_head].ffill().bfill()
        ## 数值字段
        for col in [weight_head]:
            if col:
                df[col] = pd.to_numeric(
                    df[col].replace({'-': np.nan, '': np.nan, 'N/A': np.nan, '/': np.nan}), errors='coerce'
                ).fillna(0).astype(int)
        for col in [volume_head]:
            if col:
                df[col] = pd.to_numeric(
                    df[col].replace({'-': np.nan, '': np.nan, 'N/A': np.nan, '/': np.nan}), errors='coerce'
                ).fillna(0).astype(float)
        ## 字符字段空处理
        for col in [billNo_head, subBillNo_head, dimension_head]: # 主单号、分单号字符化ad
            if col:
                df[col] = df[col].replace({'-': np.nan, 'N/A': np.nan, '/': np.nan}).fillna("").astype(str)
                if col==billNo_head: df[col] = df[col].str.replace("-", "")
        for col in [subBillNo_head]:
            df[col] = df[col].str.replace(r'[ \r\n]', "", regex=True)

        ## 对字符类型(object/string)列填充空字符串
        str_cols = df.select_dtypes(include=['object', 'string']).columns
        df[str_cols] = df[str_cols].fillna('')
        ## 对数字类型列填充0
        num_cols = df.select_dtypes(include=['int64', 'float64', 'number']).columns
        df[num_cols] = df[num_cols].fillna(0)

        ## 检查数据完整性
        def is_empty_or_na(series):
            return series.isna() | (series == '') | (series == '-') | (series == '/')
        for col in [billNo_head, flightNo_head, pingMing_head, article_head, flightNo_head, destPort_head]:
            if is_empty_or_na(df[col]).any():
                err_info = "表格中【{}】列有空行，请补充！".format(col)
                print(err_info)
                messagebox.showinfo("错误提示！", err_info)
                return -1
        # 正式开始
        taskList = set(df[billNo_head])
        taskNum = len(taskList)
        print("航班号航班日期:", flightNo_head)
        freights = df[flightNo_head].drop_duplicates(inplace=False).tolist()
        print("freights:", freights)
        cargo_types = df[cargo_type_head].drop_duplicates(inplace=False).tolist()
        print("cargo_types:", cargo_types)
        print("headless:", headless)
        # 无头规则
        if headless != "Y" and login_info.get('headless',"N") == "Y":
            headless = "Y"
        ## 要上传附件且是快件，不支持无头
        if login_info.get('upload_attachment') == "Y" and webtype!="yupei"\
            and ("快件" in ",".join(cargo_types) or "电商" in ",".join(cargo_types)):
            headless = "N"
        print("final headless:", headless)
        # input()
        # print("freights:", freights)
        freightDates = {datestrFormat(ft.split("/")[1]) for ft in freights if ft}
        print("freightDates:", freightDates)
        auto_pilot = None
        if dfs:
            print("df:", df)
            print("df.columns:", df.columns)
            if check_if_exists=="0":
                auto_pilot = Pilot(login_info=login_info,headless=headless, mapping=mp, platform=pf ,webType=webtype)
                login(auto_pilot)
            elif check_if_exists=="1" or check_if_exists=="3":  # 1,3都无头批量检查，检查后退出
                auto_pilot = Pilot(login_info=login_info,headless="Y", mapping=mp, platform=pf ,webType=webtype)
                login(auto_pilot)
                exist_dict = auto_pilot.batch_check_if_exists(taskList, freightDates)
                already_done = 0
                undone = taskList.copy()
                print("exist_dict:", exist_dict)
                if exist_dict:
                    for k, v in exist_dict.items():
                        for item in v:
                            if item.replace("-","") in taskList: 
                                #print('item.replace("-",""):', item.replace("-",""))
                                undone.remove(item.replace("-",""))
                                already_done += 1
                        print("批量检查完成，{}航班运单记录系统已存在{}条，全部单号为：\n{}".format(k, len(v), ",".join(v)))
                print("全部任务共{}条，已录入过{}条，剩余要录入{}条，原任务列表为：\n{}\n剩余任务列表为：\n{}\n".format(taskNum, already_done, taskNum-already_done, ",".join(taskList), ",".join(undone)))
                taskList = undone
            elif check_if_exists=="2":
                auto_pilot = Pilot(login_info=login_info,headless=headless, mapping=mp, platform=pf ,webType=webtype)
                login(auto_pilot)
                exist_set = auto_pilot.check_if_exists(taskList)
                print("逐个检查完成，系统中已经存在{}条，单号分别为：\n{}".format(len(exist_set), ",".join(exist_set)))
                print("剩余要录入{}条，实际任务列表为：\n{}".format(len(taskList), ",".join(taskList)))
            #input()
            print("check_if_exists:", check_if_exists)
            if len(taskList)==0 or check_if_exists=="3":
                if auto_pilot: auto_pilot.quit()
                return
            if check_if_exists == "1":  # 1 批检后，还要录单
                if auto_pilot: auto_pilot.quit()
                auto_pilot = Pilot(login_info=login_info,headless=headless, mapping=mp, platform=pf ,webType=webtype)
                login(auto_pilot)

            def run(tasks, max_retry_times=5):
                print("实际任务列表：", tasks)
                print("excel filePath:", filePath)
                # input()
                auto_pilot.bill_fill_entry(mode=webtype, df=df, taskList=tasks, sourceDataPath=filePath)
                failed = auto_pilot.get_failed()
                print("run failed:", failed)
                if not failed:
                    print("任务结束！全部录入成功！")
                    return
                # 递归本身产生循环，不需要再次while
                rest_tasks = {key for key, value in failed.items() if "超过最大尝试限制" in value or "附件上传失败" in value
                              or "发送失败" in value or "该单记录系统已经存在" not in value}
                print("rest_tasks:", rest_tasks)
                if rest_tasks:
                    max_retry_times -= 1
                    print("失败列表：", str(rest_tasks))
                    print("剩余尝试次数：", max_retry_times)
                    # 没完成任务情况下，决定是否续命继续！
                    if max_retry_times == 0:
                        is_retry = ask_retry_with_topmost(f"失败的任务：{','.join(rest_tasks)}\n失败的填单任务已重试过4次，是否还继续重试4次？(Y/N)：") or "N"
                        print("is_retry:",is_retry)
                        if is_retry.strip().upper() == "Y":
                            max_retry_times = 4
                            print("重置尝试次数为4！")
                        else:
                            print("放弃重试，后续安全退出！")
                    if max_retry_times > 0:
                        print("第{}次重次".format(5-max_retry_times))
                        run(tasks=rest_tasks, max_retry_times=max_retry_times)
                    else:
                        result_msg = "任务结束！剩余失败任务:{}".format(','.join(rest_tasks))
                        print(result_msg)
                        return

                else:
                    print("实际任务结束！全部录入成功！")
                    return

            if taskList:  #有任务才跑，针对检查再跑情况，判断是否有剩余任务
                run(taskList)
                if headless == "N":
                    res = askyesno("任务完成提醒", "任务已完成，请务必检查下记录是否正确！！", parent=root)
                    if res:
                        print("请务必检查确认下录入结果！！检查完成后关闭浏览器！！")
                        time.sleep(6000)
                    auto_pilot.quit()




if __name__ == "__main__":
    authorization()
    main()
